﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;

using System.Diagnostics;

namespace SimpleIR
{
	public sealed class IRT1T2Loop : IRT1T2Region
	{
		private readonly IRBasicBlock m_head;
		private readonly IRT1T2Block m_bbody;

		private readonly HashSet<long> m_containedblocks;

		private int m_looplastiterloc;

		public IRT1T2Loop(IRBasicBlock hb, IRT1T2Block loopstruct, HashSet<long> allblocks)
		{
			this.m_head = hb;
			this.m_bbody = loopstruct;

			this.m_containedblocks = allblocks;

			this.m_looplastiterloc = -1;
		}

		public override String ToString()
		{
			StringBuilder sb = new StringBuilder();

			sb.Append("-----Loop @ Prior = " + " Past=" + this.m_looplastiterloc + "\n");

			sb.Append("Head:\n");
			sb.Append(this.m_head.ToString());

			if(this.m_bbody != null)
			{
				sb.Append("-----Body:\n");
				sb.Append(this.m_bbody.ToString());
			}

			return sb.ToString();
		}

		public override void DGMLDisplayString(XmlWriter xwn, XmlWriter xwl, bool toplevel)
		{
			xwn.WriteStartElement("Node");
			xwn.WriteAttributeString("Id", this.GenDisplayNameString());
			xwn.WriteAttributeString("Label", "Loop -- [" + this.MinStmt().ToString() + ", " + this.MaxStmt().ToString() + "]");

			if(!toplevel)
			{
				xwn.WriteAttributeString("Category", "Summary");
				xwn.WriteAttributeString("Group", "Collapsed");
			}

			xwn.WriteEndElement();

			this.m_head.DGMLDisplayString(xwn, xwl, false);

			if(!toplevel)
			{
				xwl.WriteStartElement("Link");
				xwl.WriteAttributeString("Source", this.GenDisplayNameString());
				xwl.WriteAttributeString("Target", this.m_head.GenDisplayNameString());
				xwl.WriteAttributeString("Category", "Contains");
				xwl.WriteEndElement();
			}

			if(this.m_bbody != null)
			{
				this.m_bbody.DGMLDisplayString(xwn, xwl, false);

				if(!toplevel)
				{
					xwl.WriteStartElement("Link");
					xwl.WriteAttributeString("Source", this.GenDisplayNameString());
					xwl.WriteAttributeString("Target", this.m_bbody.GenDisplayNameString());
					xwl.WriteAttributeString("Category", "Contains");
					xwl.WriteEndElement();
				}
			}
		}

		public override string GenDisplayNameString()
		{ return "l" + this.m_head.GenDisplayNameString(); }

		public override void NumberRegion(CtrGen cg)
		{
			this.m_looplastiterloc = cg.GenNextVal();

			this.m_head.NumberRegion(cg);

			if(this.m_bbody != null)
				this.m_bbody.NumberRegion(cg);
		}

		/// <summary>
		/// The use/mod location that is less than every location in the loop but larger than the prior to entry location
		/// </summary>
		public int LoopLastIterUseModLocation
		{ get { return this.m_looplastiterloc; } }

		public override int MinStmt()
		{ return this.m_head.MinStmt(); }

		public override int MaxStmt()
		{ return (this.m_bbody != null ) ? this.m_bbody.MaxStmt() : this.m_head.MaxStmt(); }

		/// <summary>
		/// True if this loop is just a simple self loop on the head
		/// </summary>
		public bool IsSelfLoopOnHead 
		{ get { return this.m_bbody == null; } }

		/// <summary>
		/// The head block of the loop
		/// </summary>
		public IRBasicBlock Head
		{ get { return this.m_head; } }
		
		public IRT1T2Block LoopBody 
		{ 
			get 
			{
				Debug.Assert(!this.IsSelfLoopOnHead);
				return this.m_bbody; 
			} 
		}

		/// <summary>
		/// Set of all the block ids that are contained inside this loop.
		/// </summary>
		public HashSet<long> AllContainedBlocks
		{ get { return this.m_containedblocks; } }
	}
}
